﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;
using Emulator.GameBoy;
using Emulator.GameBoy.CPU;
using Emulator.GameBoyConfig;
using Microsoft.Xna.Framework.Graphics;
using Framework = Microsoft.Xna.Framework;
using Emulator.Base.CPU;
using System.Threading;

namespace Emulator
{
    public partial class Form1 : Form
    {
        Action<GameBoyConsole> UpdateDebugInfo;
        GraphicsDevice mGraphicsDevice;
        GameBoyConsole mGBConsole = new GameBoyConsole();
        Texture2D mTexture;
        SpriteBatch mSpriteBatch;
        //Stopwatch mTimer = new Stopwatch();

        UInt32[] mFrameData = new UInt32[GameBoyGPUConfig.BGWidth * GameBoyGPUConfig.BGHeight];
        StringBuilder mGBOutput = new StringBuilder();
        StringBuilder instructionsSB = new StringBuilder();
        //string breakString = "\r\n";
        public Form1()
        {
            InitializeComponent();
            PresentationParameters mPP = new PresentationParameters();
            mPP.BackBufferHeight = pcbBackground.Height;
            mPP.BackBufferWidth = pcbBackground.Width;
            mPP.DeviceWindowHandle = pcbBackground.Handle;
            mPP.DepthStencilFormat = DepthFormat.Depth24;
            mPP.PresentationInterval = PresentInterval.Immediate;
            mPP.IsFullScreen = false;

            mGraphicsDevice = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, GraphicsProfile.Reach, mPP);
            mTexture = new Texture2D(mGraphicsDevice, GameBoyGPUConfig.BGWidth, GameBoyGPUConfig.BGHeight);
            mSpriteBatch = new SpriteBatch(mGraphicsDevice);
            //mTimer = new Stopwatch();
            //mTimer.Start();
            //mGBConsole.GPU.SetTheMapData(mLogo);    
            UpdateDebugInfo = (aGBOutput =>
            {
                txtError.Text = aGBOutput.ToString();
                txtVRAM.Text = mGBConsole.OutputVRAM();
                txtCPUDebug.Text = mGBConsole.OutputCPUInfo();
                txtGPUDebug.Text = mGBConsole.OutputGPUInfo();
                txtMemory.Text = mGBConsole.OutputMemory();
                txtError.Text = mGBConsole.OutputCPUError();
            });
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                textBox1.Text = openFileDialog1.FileName;
                using (FileStream mFileContent = File.OpenRead(openFileDialog1.FileName))
                {
                    GameBoyRom mARom = new GameBoyRom(mFileContent);
                    StringBuilder mSB = new StringBuilder();
                    mSB.Append("Title:"+ mARom.RomTitle + "\r\n");
                    mSB.Append("CartridgeType:"+GBMemoryConfig.CartidgeType[mARom.CartridgeType] + "\r\n");
                    mSB.Append("RomSize:"+GBMemoryConfig.RomSize[mARom.RomSize] + "\r\n");
                    mSB.Append("RamSize:"+GBMemoryConfig.RamSize[mARom.RamSize] + "\r\n");
                    txtInfoOutput.Text = mSB.ToString();
                    
                    //Deassembly(mARom.RomContent);
                    mGBConsole.LoadRom(mARom.RomContent);
                    //mGBConsole.CurrentInstruction += aInstructionName =>
                    //{
                    //    tsCurrentInstruction.Text = aInstructionName;
                    //    instructionsSB.Append(aInstructionName);
                    //    instructionsSB.Append(breakString);
                    //};
                    //mGBConsole.Start();
                    btnStartRun.Enabled = true;
                    btnDebugRun.Enabled = true;
                    mGBConsole.VideoFrameUpdateEvent += new Action<uint[]>(obj=>DisplayVRAM(obj));
                }
            }
        }

        //private void Deassembly(byte[] aFileContent)
        //{
        //    //get 512Kbyte content to analy
        //    byte[] mEntry = new byte[4];
        //    Array.Copy(aFileContent, 0x100, mEntry, 0, 4);
        //    StringBuilder mSB = new StringBuilder();

        //    mSB.Append("Entry:");
        //    CPUInstructionBase mCurrentInstruction = InstructionMapper.SinglOpCodeConfig[mEntry[1]];
        //    mSB.Append(mCurrentInstruction.Symble);
        //    byte[] mAddress = new byte[2] { mEntry[2], mEntry[3] };
        //    mSB.Append(" " + Convert.ToString(BitConverter.ToUInt16(mAddress, 0), 16) + "\r\n");

        //    for (int i = BitConverter.ToUInt16(mAddress, 0); i < aFileContent.Length; )
        //    {
        //        byte mFirstByte = aFileContent[i];
        //        byte mSecondByte;
        //        if (i + 1 < aFileContent.Length)
        //        {
        //            mSecondByte = aFileContent[i + 1];
        //            if (mFirstByte == 0xCB)
        //            {
        //                //i++;
        //                mCurrentInstruction = InstructionMapper.MultipleOpCodeConfig[mSecondByte];
        //            }
        //            else
        //            {
        //                if (InstructionMapper.SinglOpCodeConfig.Keys.Contains(mFirstByte))
        //                    mCurrentInstruction = InstructionMapper.SinglOpCodeConfig[mFirstByte];
        //                else
        //                {
        //                    i++;
        //                    mSB.Append("Error code map:" + mFirstByte + " at address " + i);
        //                    mSB.Append("\r\n");
        //                }
        //            }
        //        }
        //        //mCurrentInstruction = InstructionConfig.SinglOpCodeConfig[mFirstByte];
        //        i += mCurrentInstruction.Length;
        //        mSB.Append(mCurrentInstruction.OpCode.ToString());
        //        mSB.Append(":");
        //        mSB.Append(mCurrentInstruction.Symble);
        //        mSB.Append(" ");
        //        mSB.Append(mCurrentInstruction.Operand);
        //        mSB.Append("\r\n");
        //    }
        //    txtAssembly.Text = mSB.ToString();
        //}

        private void DisplayVRAM(UInt32[] aVRAM)
        {
            mGraphicsDevice.Clear(Framework.Color.White);
            mGraphicsDevice.Textures[0] = null;
            mFrameData = aVRAM;
            mTexture.SetData(mFrameData);

            mSpriteBatch.Begin();
            mSpriteBatch.Draw(mTexture, new Framework.Vector2(0, 0), Framework.Color.White);
            mSpriteBatch.End();

            mGraphicsDevice.Present();
#if DEBUG
            this.Invoke(UpdateDebugInfo,new object[]{mGBConsole});
#endif
        }

        private void btnSetCPUFre_Click(object sender, EventArgs e)
        {
            int mTimerFrequence = 100;
            if(int.TryParse(txtCPUFre.Text,out mTimerFrequence))
                mGBConsole.SetTimerFrequence(mTimerFrequence);
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            if (btnStop.Tag.ToString() == "Running")
            {
                mGBConsole.Stop();
                txtError.Text = instructionsSB.ToString();
                btnStop.Text = "继续";
                btnStop.Tag = "Stop";
                btnStepOne.Enabled = true;
            }
            else
            {
                btnStop.Text = "停止运行";
                btnStop.Tag = "Running";
                btnStepOne.Enabled = false;
                btnSetCPUFre_Click(sender, e);
            }
        }

        private void btnStepOne_Click(object sender, EventArgs e)
        {
            if (btnStop.Tag.ToString() == "Stop")
            {
#if DEBUG
                mGBConsole.DebugFlag = true;
#endif
                btnSetCPUFre_Click(sender, e);
                txtError.Text = instructionsSB.ToString();
                txtError.Focus();
                txtError.ScrollToCaret();
            }
        }

        private void btnStartRun_Click(object sender, EventArgs e)
        {
            //ThreadPool.QueueUserWorkItem(obj => mGBConsole.Start());
            mGBConsole.Start();
            btnStartRun.Enabled = false;
            btnDebugRun.Enabled = false;
            btnStop.Tag = "Running";
        }

        private void btnDebugRun_Click(object sender, EventArgs e)
        {
            txtError.Text = instructionsSB.ToString();
            btnStartRun.Enabled = false;
            btnDebugRun.Enabled = false;
            btnStop.Tag = "Stop";
            btnStepOne_Click(sender, e);
            btnStepOne.Enabled = true;
        }
    }
}
